﻿using Nop.Core;
using Nop.Core.Data;
using Nop.Core.Domain.WindAlert;
using Nop.Data;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading;

namespace Nop.Services.WindAlert
{
    /// <summary>
    /// Winddata service
    /// </summary>
    public partial class WaveDataService : IWaveDataService
    {
        #region Fields

        private readonly IRepository<WaveData> _waveDataRepository;
        private readonly IDbContext _dbContext;
        private readonly IWindHelpers _windHelper;
        #endregion

        #region Ctor

        /// <summary>
        /// Ctor
        /// </summary>
        /// <param name="windDataRepository">Live Station repository</param>
        public WaveDataService(IRepository<WaveData> waveDataRepository
            ,IDbContext dbContext, IWindHelpers windHelper)
        {
            this._waveDataRepository = waveDataRepository;
            this._dbContext = dbContext;
            this._windHelper = windHelper;
        }
        #endregion

        #region Methods

        /// <summary>
        /// Clears a log
        /// </summary>
        public virtual void ClearAll()
        {
            var waveData = _waveDataRepository.Table.ToList();
            foreach (var wdItem in waveData)
                _waveDataRepository.Delete(wdItem);
            //setIdentity();
        }

        /// <summary>
        /// Deletes a windData
        /// </summary>
        /// <param name="windData">WindData</param>
        public virtual void DeleteWaveData(WaveData waveData)
        {
            if (waveData == null)
                throw new ArgumentNullException("waveData");

            _waveDataRepository.Delete(waveData);
        }

        /// <summary>
        /// Gets all waveDatas
        /// </summary>
        /// <param name="showHidden">A value indicating whether to show hidden records</param>
        /// <returns>wave data collection</returns>
        public virtual IList<WaveData> GetAllWaveData()
        {
                var query = from m in _waveDataRepository.Table
                            select m;
                var waveDatas = query.ToList();
                return waveDatas;
        }

        /// <summary>
        /// Gets all waveDatas
        /// </summary>
        /// <param name="pageIndex">Page index</param>
        /// <param name="pageSize">Page size</param>
        /// <param name="showHidden">A value indicating whether to show hidden records</param>
        /// <returns>wave data</returns>
        public virtual IPagedList<WaveData> GetAllWaveData(int pageIndex, int pageSize)
        {
            var waveDatas = GetAllWaveData();
            return new PagedList<WaveData>(waveDatas, pageIndex, pageSize);
        }


        /// <summary>
        /// Gets all wavedata items
        /// </summary>
        /// <param name="fromUtc">wavedata item creation from; null to load all records</param>
        /// <param name="toUtc">wavedata item creation to; null to load all records</param>
        /// <param name="pageIndex">Page index</param>
        /// <param name="pageSize">Page size</param>
        /// <returns>wavedata item collection</returns>
        public virtual IPagedList<WaveData> GetAllWaveData(DateTime? fromUtc, DateTime? toUtc, int pageIndex, int pageSize)
        {
            var query = _waveDataRepository.Table;
            if (fromUtc.HasValue)
                query = query.Where(wd => fromUtc.Value <= wd.CreatedOnUtc);
            if (toUtc.HasValue)
                query = query.Where(wd => toUtc.Value >= wd.CreatedOnUtc);
            
            query = query.OrderByDescending(wd => wd.Id);
           // query = query.OrderByDescending(wd => wd.CreatedOnUtc);

            var waveData = new PagedList<WaveData>(query, pageIndex, pageSize);
            return waveData;
        }

        /// <summary>
        /// Gets a waveData
        /// </summary>
        /// <param name="waveDataId">wave data identifier</param>
        /// <returns>wave data</returns>
        public virtual WaveData GetWaveDataById(int waveDataId)
        {
            if (waveDataId == 0)
                return null;
            var waveData = _waveDataRepository.GetById(waveDataId);
            return waveData;
        }

        /// <summary>
        /// Get wave data items by identifiers
        /// </summary>
        /// <param name="waveataIds">wavedata item identifiers</param>
        /// <returns>Wavedata items</returns>
        public virtual IList<WaveData> GetWaveDataByIds(int[] waveaDataIds)
        {
            if (waveaDataIds == null || waveaDataIds.Length == 0)
                return new List<WaveData>();

            var query = from wd in _waveDataRepository.Table
                        where waveaDataIds.Contains(wd.Id)
                        select wd;
            var waveDataItems = query.ToList();
            //sort by passed identifiers
            var sortedWaveDataItems = new List<WaveData>();
            foreach (int id in waveaDataIds)
            {
                var waveData = waveDataItems.Find(x => x.Id == id);
                if (waveData != null)
                    sortedWaveDataItems.Add(waveData);
            }
            return sortedWaveDataItems;
        }

        /// <summary>
        /// Get wwave data itemby spot Id
        /// </summary>
        /// <param name="SpotIds">spot identifiers</param>
        /// <returns>Wavedata items</returns>
        public virtual IList<WaveData> GetWaveDataBySpotId(int spotId, int backHours=0)
        {
            if (spotId < 1)
                return new List<WaveData>();

            var query = from wd in _waveDataRepository.Table
                        where wd.SpotId == spotId
                        select wd;

            if(backHours != 0)
            {
                var compareDate = DateTime.Now.AddHours(backHours);
                query = from wd in query
                        where wd.WaveAdjustedTime > compareDate
                        select wd;
            }
            var waveDataItems = query.ToList();
            return waveDataItems;
        }

        /// <summary>
        /// Get wave data item by spot Id top 1
        /// </summary>
        /// <param name="spotIds">spot identifiers</param>
        /// <returns>wavedata items</returns>
        public virtual WaveData GetWaveDataBySpotIdTop1(int spotId)
        {
            if (spotId <= 0)
                return new WaveData();

            var query = (from wd in _waveDataRepository.Table
                        where wd.SpotId == spotId
                        select wd).OrderByDescending(wd => wd.Id).FirstOrDefault();
            return query;
        }

        /// <summary>
        /// Inserts a waveData
        /// </summary>
        /// <param name="waveData">Wave data</param>
        public virtual void InsertWaveData(WaveData waveData)
        {
            if (waveData == null)
                throw new ArgumentNullException("waveData");

            _waveDataRepository.Insert(waveData);
        }

        #endregion

        #region update spot wave

        

        public virtual WaveData UpdateWaveSpot(Spot spot)
        {
            var dataRow = _windHelper.GetPageData(spot.WaveDataUrl);
            var waveData = new WaveData();
            waveData = SplitWaveData(dataRow, spot.WaveAdjustTime);
            if (waveData == null)
                return null;
            waveData.SpotId = spot.Id;
            waveData.CreatedOnUtc = DateTime.Now;
            return waveData;
        }
        #endregion 

        #region Split methods

        private WaveData SplitWaveData(string strAllData, int addHoours = 0)
        {
            var WaveData = new WaveData();
            try
            {
                HtmlAgilityPack.HtmlDocument doc = new HtmlAgilityPack.HtmlDocument();
                doc.LoadHtml(strAllData);

                //get current date
                var waveTableCurrentDateString = doc.DocumentNode.SelectNodes("//center")
                                                .Skip(1)
                                                .FirstOrDefault().InnerText ;

                Thread.CurrentThread.CurrentCulture = new CultureInfo("he-IL");
                DateTime waveTableCurrentDate = Convert.ToDateTime(waveTableCurrentDateString);

                List<List<string>> table = doc.DocumentNode.SelectSingleNode("//table")
                            .Descendants("tr")
                            .Skip(1)
                            .Where(tr => tr.Elements("td").Count() > 1)
                            .Select(tr => tr.Elements("td").Select(td => td.InnerText.Trim()).ToList())
                            .ToList();

                List<string> tableRow = table.Last();
                
                //Date and Time
                string[] TimeCell = Regex.Split(tableRow[0], ":");
                var intHour = Convert.ToInt32(TimeCell[0]);
                var intMinutes = Convert.ToInt32(TimeCell[1]);
                double numberDouble = 0;
                int numberInt = 0;

                WaveData.WaveTimeUTC = waveTableCurrentDate.Date.Add(new TimeSpan(intHour, intMinutes, 0));
                WaveData.WaveAdjustedTime = WaveData.WaveTimeUTC.AddHours(addHoours);

                WaveData.Hmax = (double.TryParse(tableRow[1], out numberDouble)) ? numberDouble : 0;
                WaveData.Hs = (double.TryParse(tableRow[2], out numberDouble)) ? numberDouble : 0;
                WaveData.Hthird = (double.TryParse(tableRow[3], out numberDouble)) ? numberDouble : 0;
                WaveData.Direction = (int.TryParse(tableRow[4], out numberInt)) ? numberInt : 0;
                WaveData.Tav = (double.TryParse(tableRow[5], out numberDouble)) ? numberDouble : 0;
                WaveData.Tz = (double.TryParse(tableRow[6], out numberDouble)) ? numberDouble : 0;
                WaveData.Tp = (double.TryParse(tableRow[7], out numberDouble)) ? numberDouble : 0;
                WaveData.Temperture = (double.TryParse(tableRow[8], out numberDouble)) ? numberDouble : 0;

            }
            catch (Exception ex)
            {
                string t = ex.Message;
                return null;
            }
            return WaveData;
        }
        #endregion
    }
}
